#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include "fourier_transform.hpp"



namespace LuaNumeric {
	typedef std::complex<double> complex;
	typedef std::vector<double> vector;
	typedef std::vector<complex> cvector;
	typedef std::shared_ptr<vector> pvector;
	typedef std::shared_ptr<cvector> pcvector;

	//std::complex<double> fx2kx( const VecX& xx, const VecWx& wx, double kx, double dx );


	void open_luaFT(sol::table& module) {
		module.set_function("fx2kx", sol::overload(
						[](vector xx, cvector wx, double kx, double dx){
							  return FT::fx2kx( xx, wx, kx, dx );
						},
						[](pvector xx, pcvector wx, pvector kx, double dx, pcvector res){
							  return FT::fx2kx( *xx, * wx, *kx, dx, *res );
						},
						[](vector& xx, cvector& wx, vector& kx, double dx, cvector& res){
							  return FT::fx2kx( xx, wx, kx, dx, res );
						}
			)
		);

		module.set_function("ft1D", sol::overload(
						[](vector xx, cvector wx, double kx, double dx){
							  return FT::ft1D( xx, wx, kx, dx );
						},
						[](pvector xx, pcvector wx, pvector kx, double dx, pcvector res){
							  return FT::ft1D( *xx, * wx, *kx, dx, *res );
						},
						[](const vector& tt, const vector& vt, const vector& ww, double dt){
							cvector res(ww.size());
							FT::ft1D( tt, vt, ww, dt, res );
							return res;
						},
						[](vector& xx, cvector& wx, vector& kx, double dx, cvector& res){
							  return FT::ft1D( xx, wx, kx, dx, res );
						},
						[](const vector& tt, const cvector& vt, double dt,const vector& ww){
							cvector res(ww.size());
							FT::ft1D( tt, vt, ww, dt, res );
							return res;
						},
						[](const vector& tt, const cvector& vt,const vector& ww, double dt){
							cvector res(ww.size());
							FT::ft1D( tt, vt, ww, dt, res );
							return res;
						}
			)
		);

		module.set_function("ift1D", sol::overload(

						[](vector ww, cvector vw, double tt, double dw){
							  return FT::ift1D( ww, vw, tt, dw );
						},
						[](pvector xx, pcvector wx, pvector kx, double dx, pcvector res){
							  return FT::ift1D( *xx, * wx, *kx, dx, *res );
						},
						[](vector& ww, cvector& vw, vector& tt, double dw, cvector& res){
							  return FT::ift1D( ww, vw, tt, dw, res );
						},
						[](vector& ww, cvector& vw, double dw, vector& tt, cvector& res){
							  return FT::ift1D( ww, vw, tt, dw, res );
						},
						[](vector& ww, cvector& vw, vector& tt, double dw){
							cvector res(tt.size());
							FT::ift1D( ww, vw, tt, dw, res );
							return res;
						},
						[](vector& ww, cvector& vw, double dw, vector& tt){
							cvector res(tt.size());
							FT::ift1D( ww, vw, tt, dw, res );
							return res;
						}
			)
		);

		module.set_function("conv1D", sol::overload(
						[](vector& tt, cvector& vt, double dt,
						   vector& ww, cvector& vw, double dw,
						   vector& ttRes){
						  cvector vtRes(ttRes.size());
						  FT::conv1D( tt, vt, dt,
								  	  ww, vw, dw, ttRes, vtRes );
						  return vtRes;
						},
						[](vector& tt, vector& vt, double dt,
						   vector& ww, cvector& vw, double dw,
						   vector& ttRes){
						  cvector vtRes(ttRes.size());
						  FT::conv1D( tt, vt, dt,
									  ww, vw, dw, ttRes, vtRes );
						  return vtRes;
						},
						[](vector& tt, cvector& vt, double dt,
						   vector& ww, cvector& vw, double dw,
						   vector& ttRes){
						  cvector vtRes(ttRes.size());
						  FT::conv1D( tt, vt, dt,
									  ww, vw, dw, ttRes, vtRes );
						  return vtRes;
						},
						[](vector& tt, cvector& vt, double dt,
						   vector& ww, cvector& vw,
						   vector& ww1,double dw,
						   vector& ttRes){
						  cvector vtRes(ttRes.size());
						  auto wres = FT::conv1D( tt, vt, dt,
									  ww, vw,
									  ww1,dw, ttRes, vtRes );
						  return std::make_tuple(vtRes,wres);
						},
						[](vector& tt, cvector& vt,
						   vector& ww, cvector& vw,
						   double ws, double we, int nw,
						   vector& ttRes){
						  cvector vtRes(ttRes.size());
						  auto wres = FT::conv1D( tt, vt,
									  ww, vw,
									  ws,we,nw, ttRes, vtRes );
						  return std::make_tuple(vtRes,wres);
						}
			)
		);




		module.set_function("ft2D", sol::overload(
						[](const vector& xx, const vector& yy, const cvector& vv, double kx, double ky, double ds){
							  return FT::ft2D(xx, yy, vv, kx, ky, ds );
						},
						[](const vector& xx,const vector& yy, const cvector& vv,
								const vector& kx, const vector& ky, double ds, cvector& res){
							  return FT::ft2D(xx, yy, vv, kx, ky, ds,res );
						},
						[](const vector& xx,const vector& yy, const cvector& vv,
								const vector& kx, const vector& ky, const vector& ds, cvector& res){
							  return FT::ft2D(xx, yy, vv, kx, ky, ds,res );
						}
			)
		);
		module.set_function("fft2D", sol::overload(
						[](Eigen::MatrixXd& in, Eigen::MatrixXcd& out){
							FT::efft(in,out);
						}
			)
		);

	}
}

namespace FT{
	void efft(Eigen::MatrixXd& in, Eigen::MatrixXcd& out)
	{
		Eigen::FFT<double> fft;
		size_t dim_x = in.rows();
		size_t dim_y = in.cols();

		out.setZero(dim_x, dim_y);

		for (int k = 0; k < in.rows(); k++) {
			Eigen::VectorXcd tmpOut(dim_x);
			fft.fwd(tmpOut, in.row(k));
			out.row(k) = tmpOut;
		}

		for (int k = 0; k < in.cols(); k++) {
			Eigen::VectorXcd tmpOut(dim_y);
			fft.fwd(tmpOut, out.col(k));
			out.col(k) = tmpOut;
		}
	}
}


